/***************************************************************************//**
 * @file MSc_DataNode.cpp
 * @author  Marek M. Cel <marekcel@mscsim.org>
 *
 * @section LICENSE
 *
 * Copyright (C) 2014 Marek M. Cel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/
#ifndef MSC_DATA_NODE_CPP
#define MSC_DATA_NODE_CPP
#endif

////////////////////////////////////////////////////////////////////////////////

#include <limits>

#include <utils/MSc_DataNode.h>
#include <utils/MSc_Utils.h>

////////////////////////////////////////////////////////////////////////////////

MSc_DataNode::MSc_DataNode() :
    m_parent( 0 ),
    m_type( Group )
{
    m_children.clear();
}

////////////////////////////////////////////////////////////////////////////////

MSc_DataNode::~MSc_DataNode()
{
    if ( m_type == Group )
    {
        DataNodes::iterator it;

        it = m_children.begin();

        while ( it != m_children.end() )
        {
            if ( it->second )
            {
                delete it->second;
                it->second = 0;
            }

            ++it;
        }
    }

    m_children.clear();
}

////////////////////////////////////////////////////////////////////////////////

int MSc_DataNode::addNode( std::string path, Type type )
{
    if ( m_type == Group )
    {
        stripPathSlashes( path );

        if ( path.length() > 0 )
        {
            MSc_Utils::strToLower( path );

            std::string pathLead;
            std::string pathRest;

            breakPath( path, pathLead, pathRest );

            if ( pathRest.size() > 0 )
            {
                MSc_DataNode *node = 0;

                if ( m_children.count( pathLead ) == 0 )
                {
                    addNode( pathLead, Group );
                }

                node = findNode( pathLead );

                if ( node )
                {
                    return node->addNode( pathRest, type );
                }
            }
            else
            {
                if ( pathLead.size() > 0 )
                {
                    if ( m_children.count( pathLead ) == 0 )
                    {
                        MSc_DataNode *node = createNode( pathLead, type, this );

                        std::pair<DataNodes::iterator,bool> result;

                        result = m_children.insert( std::pair<std::string,MSc_DataNode*>( pathLead, node ) );

                        if ( result.second == true )
                        {
                            return MSCFDM_SUCCESS;
                        }
                        else
                        {
                            delete node;
                        }
                    }
                }
            }
        }
    }

    return MSCFDM_FAILURE;
}

////////////////////////////////////////////////////////////////////////////////

bool MSc_DataNode::getDatab() const
{
    if ( m_type == Bool )
    {
        return m_data.bData;
    }

    return std::numeric_limits< bool >::quiet_NaN();
}

////////////////////////////////////////////////////////////////////////////////

int MSc_DataNode::getDatai() const
{
    if ( m_type == Int )
    {
        return m_data.iData;
    }

    return std::numeric_limits< int >::quiet_NaN();
}

////////////////////////////////////////////////////////////////////////////////

long MSc_DataNode::getDatal() const
{
    if ( m_type == Long )
    {
        return m_data.lData;
    }

    return std::numeric_limits< long >::quiet_NaN();
}

////////////////////////////////////////////////////////////////////////////////

float MSc_DataNode::getDataf() const
{
    if ( m_type == Float )
    {
        return m_data.fData;
    }

    return std::numeric_limits< float >::quiet_NaN();
}

////////////////////////////////////////////////////////////////////////////////

double MSc_DataNode::getDatad() const
{
    if ( m_type == Double )
    {
        return m_data.dData;
    }

    return std::numeric_limits< double >::quiet_NaN();
}

////////////////////////////////////////////////////////////////////////////////

MSc_DataNode* MSc_DataNode::getNode( std::string path )
{
    MSc_Utils::strToLower( path );

    return findNode( path );
}

////////////////////////////////////////////////////////////////////////////////

std::string MSc_DataNode::getPath() const
{
    std::string path;
    path.clear();

    if ( m_parent )
    {
        path += m_parent->getPath();
    }

    path += "/";
    path += m_name;

    return path;
}

////////////////////////////////////////////////////////////////////////////////

double MSc_DataNode::getValue() const
{
    if ( m_type == Bool   ) return (double)m_data.bData;
    if ( m_type == Int    ) return (double)m_data.iData;
    if ( m_type == Long   ) return (double)m_data.fData;
    if ( m_type == Float  ) return (double)m_data.lData;
    if ( m_type == Double ) return m_data.dData;

    return std::numeric_limits< double >::quiet_NaN();
}

////////////////////////////////////////////////////////////////////////////////

int MSc_DataNode::setDatab( bool value )
{
    if ( m_type == Bool )
    {
        m_data.bData = value;
        return MSCFDM_SUCCESS;
    }

    return MSCFDM_FAILURE;
}

////////////////////////////////////////////////////////////////////////////////

int MSc_DataNode::setDatai( int value )
{
    if ( m_type == Int )
    {
        m_data.iData = value;
        return MSCFDM_SUCCESS;
    }

    return MSCFDM_FAILURE;
}

////////////////////////////////////////////////////////////////////////////////

int MSc_DataNode::setDatal( long value )
{
    if ( m_type == Long )
    {
        m_data.lData = value;
        return MSCFDM_SUCCESS;
    }

    return MSCFDM_FAILURE;
}

////////////////////////////////////////////////////////////////////////////////

int MSc_DataNode::setDataf( float value )
{
    if ( m_type == Float )
    {
        m_data.fData = value;
        return MSCFDM_SUCCESS;
    }

    return MSCFDM_FAILURE;
}

////////////////////////////////////////////////////////////////////////////////

int MSc_DataNode::setDatad( double value )
{
    if ( m_type == Double )
    {
        m_data.dData = value;
        return MSCFDM_SUCCESS;
    }

    return MSCFDM_FAILURE;
}

////////////////////////////////////////////////////////////////////////////////

void MSc_DataNode::breakPath( std::string path, std::string &pathLead, std::string &pathRest )
{
    size_t pos = path.find( '/' );

    pathLead.clear();

    if ( pos != std::string::npos )
    {
        pathLead = path.substr( 0 , pos );
        pathRest = path.substr( pos + 1 );
    }
    else
    {
        pathLead = path;
        pathRest.clear();
    }
}

////////////////////////////////////////////////////////////////////////////////

MSc_DataNode* MSc_DataNode::createNode( std::string name, Type type, MSc_DataNode *parent )
{
    MSc_Utils::strToLower( name );

    MSc_DataNode *node = new MSc_DataNode();

    node->m_parent = parent;

    node->m_name = name;
    node->m_type = type;

    switch ( node->m_type )
    {
        case Group:  node->m_children.clear();    break;
        case Bool:   node->m_data.bData = false;  break;
        case Int:    node->m_data.iData = 0;      break;
        case Long:   node->m_data.lData = 0L;     break;
        case Float:  node->m_data.fData = 0.0f;   break;
        case Double: node->m_data.dData = 0.0;    break;
    }

    return node;
}

////////////////////////////////////////////////////////////////////////////////

MSc_DataNode* MSc_DataNode::findNode( std::string path )
{
    stripPathSlashes( path );

    if ( path.length() > 0 )
    {
        std::string pathLead;
        std::string pathRest;

        breakPath( path, pathLead, pathRest );

        DataNodes::iterator it;

        it = m_children.find( pathLead );

        if ( it != m_children.end() )
        {
            if ( pathRest.size() > 0 )
            {
                return it->second->findNode( pathRest );
            }
            else
            {
                return it->second;
            }
        }
    }

    return NULL;
}

////////////////////////////////////////////////////////////////////////////////

void MSc_DataNode::stripPathSlashes( std::string &path )
{
    size_t pos;

    // removing leading slashes
    pos = path.find( '/' );

    if ( pos != std::string::npos )
    {
        while ( pos == 0 )
        {
            path   = path.substr( pos + 1 );
            pos    = path.find( '/' );
        }
    }

    // removing trailing slashes
    pos = path.rfind( '/' );

    if ( pos != std::string::npos )
    {
        while ( pos == path.length() - 1 )
        {
            path   = path.substr( 0, pos );
            pos    = path.rfind( '/' );
        }
    }
}
